// Copyright 2010 Google Inc. All Rights Reserved.

/**
 * @fileoverview Client-side code for making an API request across domains.
 *
 * @author monsur@google.com (Monsur Hossain)
 */

goog.provide('googleapis.ApiClient');


/**
 * @constructor
 */
googleapis.ApiClient = function() {
};


/**
 * @type {string}
 * @private
 */
googleapis.ApiClient.DELETE_METHOD_ = '.delete';


/**
 * A mapping of api name to the version it supports.  This can be updated
 * through the config.
 * @type {Object}
 * @private
 */
googleapis.ApiClient.versionMap_ = {};


/**
 * Register an API method. The method "buzz.people.get" will be exposed as:
 * - buzz.people.get()
 * - googleapis.people.get()
 * - osapi.people.get()
 * - google.people.get()
 *
 * We will remove options once we decide what we like.
 *
 * @param {string} method The method name.
 */
googleapis.ApiClient.register = function(method) {
  // TODO(monsur): The code below exports the same method in three different
  // locations.  For example, chili.activities.search is exported to:
  //   * chili.activities.search
  //   * osapi.chili.activities.search
  //   * googleapis.activities.search
  // We should consolidate these into a single set of methods that everyone
  // uses (and remove the others).
  osapi._registerMethod(method, {'name': 'googleapis',
      'execute': googleapis.ApiClient.execute_});
  var api = goog.getObjectByName(method, osapi);
  goog.exportSymbol(method, api);

  // Replace methods named "delete" with "remove", since "delete" is a
  // reserved JavaScript word.
  // TODO(monsur): This should be done first, before the methods above are
  // exported.
  var localMethod = method.substring(method.indexOf('.') + 1);
  if (googleapis.endsWith(localMethod, googleapis.ApiClient.DELETE_METHOD_)) {
    localMethod =
        localMethod.replace(googleapis.ApiClient.DELETE_METHOD_, '.remove');
  }

  goog.exportSymbol('googleapis.' + localMethod, api);
};


/**
 * Set the versions to use for each API.
 * @param {Object} obj A mapping of service name to version, for example:
 *     {'chili': 'v1'}.
 */
googleapis.ApiClient.setVersions = function(obj) {
  for (var name in obj) {
    if (!obj.hasOwnProperty(name)) {
      continue;
    }
    googleapis.ApiClient.versionMap_[name] = obj[name];
  }
};


/**
 * @param {string} service The service name.
 * @return {string} The service version, default is "v1".
 * @private
 */
googleapis.ApiClient.getVersion_ = function(service) {
  return googleapis.ApiClient.versionMap_[service];
};


/**
 * Adds auth field to the headers object.
 * @param {?Object} opt_headers Optional headers object.
 * @return {Object} Headers object with Auth included if applicable.
 */
googleapis.ApiClient.addAuthHeader = function(opt_headers) {
  var headers = opt_headers || {};
  var headerVal = null;
  if ('auth' in window['googleapis']) {
    // TODO(monsur): OAuth2 hasn't launched publicly yet.
    // TODO(monsur): Standardize on OAuth2 auth header once support for it has
    // been pushed everywhere.
    var token = window['googleapis']['auth']['getToken']();
    if (token) {
      headerVal = 'WRAP access_token=' + token['access_token'];
    }
  }
  if (headerVal) {
    headers['Authorization'] = headerVal;
  }
  return headers;
};


/**
 * Adds X-ClientDetails field to the headers object.
 * @param {?Object} opt_headers Optional headers object.
 * @return {Object} Headers object with ClientDetails included.
 */
googleapis.ApiClient.addClientDetailsHeader = function(opt_headers) {
  var headers = opt_headers || {};
  if (window['navigator']) {
    var fields = ['appVersion', 'platform', 'userAgent'];
    var clientDetails = [];
    for (var i = 0; i < fields.length; i++) {
      if (window['navigator'][fields[i]]) {
        clientDetails.push(
            encodeURIComponent(fields[i]) + '=' +
            encodeURIComponent(window['navigator'][fields[i]]));
      }
    }
    headers['X-ClientDetails'] = clientDetails.join('&');
  }
  return headers;
};


/**
 * @param {Array.<Object>} requests An array of RPC requests.
 * @param {Function} callback The callback to execute with the response.
 * @private
 */
googleapis.ApiClient.execute_ = function(requests, callback) {
  // TODO(uidude) Clean this up
  for (var i = 0; i < requests.length; i++) {
    var request = requests[i];
    var service = googleapis.getServiceNameFromMethod(request['method']);
    request['jsonrpc'] = '2.0';
    // TODO(monsur): Is "key" used?
    request['key'] = request['id'];
    var version = googleapis.ApiClient.getVersion_(service);
    if (version) {
      request['apiVersion'] = version;
    }
    delete(request['params']['userId']);
  }

  var headers = {};
  headers = googleapis.ApiClient.addAuthHeader(headers);
  headers = googleapis.ApiClient.addClientDetailsHeader(headers);

  var requestParams = {
    'requests': requests,
    'headers': headers
  };

  googleapis.Transport.sendRequest('makeRequest', requestParams, callback);
};


/**
 * Initialize the client from the config.
 */
googleapis.ApiClient.init = function() {
  var config = googleapis.Config.get();
  for (var api in config['methods']) {
    googleapis.ApiClient.register(api);
  }
  if ('versions' in config) {
    googleapis.ApiClient.setVersions(config['versions']);
  }
  gadgets.rpc.register('ready', function() {
    googleapis.Transport.onProxyReady();
  });
};


// Always initialize client code - this is low cost.
gadgets.config.register('googleapis', null, function() {
  googleapis.ApiClient.init();
});
